# this is the user (payer / payee) module
# 
# this module contains the following classes:
# 
#   User  - define a single user

from __init__ import _ABC_AutoID_


class User(_ABC_AutoID_):
    """
    User (payer / payee) class

    One user can be attached to multiple profiles. Users cannot
    be deleted, but can be marked as inactive. An user can only
    be marked as inactive if he/she has no outstanding balance
    in any linked profiles. Once an user is marked as inactive,
    he/she will be removed from all linked profiles and cannot
    be added to any profile until being marked as active again.
    """

    ######## Class-level variable ########

    _user_map_ = {
        # this maps the name / alias to the actual user
        }


    ######## Overridden build-in Methods / Operators ########

    def __init__(self, name):
        super(User, self).__init__()

        self.__changeName(name, new=True) # this will perform name check inside

        self.__aliases = [ ]      # no aliases yet
        self.__active = False
        return

    def __str__(self):
        return "[{2}] {0} - {1}".format(
            self._id_, self.__name,
            self.__active and " Active " or "Inactive"
            )
    def __repr__(self):
        return "{{'id':{0},'name':{1},'active':{2},'aliases':[{3}]}}".format(
            self._id_, self.__name, self.__active, self.getAliases(',')
            )

    def __eq__(self, other):
        if self is None or other is None:
            return False
        return self._id_ == other._id_
    def __ne__(self, other):
        if self is None or other is None:
            return True 
        return self._id_ != other._id_
    def __lt__(self, other):
        return self._id_ < other._id_
    def __le__(self, other):
        return self._id_ <= other._id_
    def __gt__(self, other):
        return self._id_ > other._id_
    def __ge__(self, other):
        return self._id_ >= other._id_


    ######## Public Functions ########

    def debug(self):
        return "[{0}] '{1}' ==> [ {2} ]".format(
            self._id_, self.__name, self.getAliases()
            )

    @classmethod
    def getUser(cls, name):
        if name in cls._user_map_:
            return cls._user_map_[name]
        return None

    @classmethod
    def getAllUsers(cls):
        return [ cls.getObject(uid)
                 for uid in sorted(cls._obj_dict_.iterkeys())
                 ]


    ######## Getters / Setters ########

    def isActive(self):
        return self.__active

    def activate(self):
        self.__active = True
        return

    def deactivate(self):
        self.__active = False
        return


    def getID(self):
        return super(User, self).getID()

    def getName(self):
        return self.__name

    def changeName(self, name):
        self.__changeName(name, new=False)
        return

    def getAliasList(self):
        return [ alias for alias in self.__aliases ]

    def getAliases(self, dlm=', '):
        return dlm.join(self.__aliases)

    def addAlias(self, alias):
        if alias in User._user_map_: # check name conflict
            raise ValueError('user "{0}" already exists in the system'.format(alias))
        self.__aliases.append(alias)
        User._user_map_[alias] = self
        return

    def removeAlias(self, alias):
        if alias not in self.__aliases:
            return False
        self.__aliases.remove(alias)
        del User._user_map_[alias]
        return True


    ######## Override Protected Member Functions ########

    @classmethod
    def _dump_(cls):
        return super(User, cls)._dump_()

    @classmethod
    def _load_(cls, objDump):
        super(User, cls)._load_(objDump)
        # re-build user mapping
        cls._user_map_ = { }    # re-init the user mapping
        for usr in cls._obj_dict_.itervalues():
            cls._user_map_[usr.getName()] = usr
            for alias in usr.getAliasList():
                cls._user_map_[alias] = usr
        return


    ######## Private Member Functions ########

    def __changeName(self, name, new=False):
        if name in User._user_map_:     # check name conflict
            raise ValueError('user "{0}" already exists in the system'.format(name))

        if not new:                     # remove old name from the mapping
            del User._user_map_[self.__name]
        self.__name = name              # update the name
        User._user_map_[name] = self    # add the new name to the mapping
        return
